/**
 *
 */
package co.fitstart.payment.service.impl;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.demo2do.core.BusinessException;
import com.demo2do.core.persistence.GenericJdbcSupport;

import co.fitstart.payment.dao.TransactionRecordDaoSupport;
import co.fitstart.payment.entity.app.App;
import co.fitstart.payment.entity.channel.Channel;
import co.fitstart.payment.entity.transaction.OperationType;
import co.fitstart.payment.entity.transaction.TransactionLog;
import co.fitstart.payment.entity.transaction.TransactionRecord;
import co.fitstart.payment.entity.transaction.TransactionStatus;
import co.fitstart.payment.service.TransactionRecordService;

/**
 * @author lute
 *
 */
@Service("transactionRecordService")
public class TransactionRecordServiceImpl implements TransactionRecordService {

    @Autowired
    private GenericJdbcSupport genericJdbcSupport;

    @Autowired
    private TransactionRecordDaoSupport transactionRecordDaoSupport;

    /* (non-Javadoc)
     * @see cn.xcm.payment.service.TransactionRecordService#createTransactionRecord(cn.xcm.payment.entity.transaction.TransactionRecord)
     */
    public void createTransactionRecord(TransactionRecord transactionRecord) {

        // Get transaction record from database according to the request number of passed in transaction record
        // to check if there has been any transaction records existed belongs to the request number
        TransactionRecord existedTransactionRecord = transactionRecordDaoSupport.getTransactionRecord(transactionRecord.getRequestNo());

        if (existedTransactionRecord != null) {

            // FIXME Currently I can just throw the business exception and there's no elegant strategy to handle it properly
            throw new BusinessException("Duplicated request number for creating new transaction record");
        }

        // Create transaction record
        String sentence = "INSERT INTO transaction_record (app_id, channel_id, amount, order_no, request_no, trade_no, card_no, transaction_status, last_modified_time) VALUES (:appId, :channelId, :amount, :orderNo, :requestNo, :tradeNo, :cardNo, :transactionStatusAsInt, :lastModifiedTime)";
        genericJdbcSupport.executeSQL(sentence, transactionRecord);

        // Get the newly inserted transaction record
        TransactionRecord newTransactionRecord = transactionRecordDaoSupport.getTransactionRecord(transactionRecord.getRequestNo());

        // Create transaction log
        sentence = "INSERT INTO transaction_log (transaction_record_id, operation_type, modified_time) VALUES (:transactionRecordId, :operationTypeAsInt, :modifiedTime)";
        genericJdbcSupport.executeSQL(sentence, new TransactionLog(newTransactionRecord, OperationType.CREATE));
    }

    /* (non-Javadoc)
     * @see cn.xcm.payment.service.TransactionRecordService#getTransactionRecord(java.lang.String)
     */
    public TransactionRecord getTransactionRecord(String requestNo) {
       return transactionRecordDaoSupport.getTransactionRecord(requestNo);
    }

    /* (non-Javadoc)
     * @see co.fitstart.payment.service.TransactionRecordService#getPaidTransactionRecord(co.fitstart.payment.entity.app.App, co.fitstart.payment.entity.channel.Channel, java.lang.String)
     */
    public TransactionRecord getPaidTransactionRecord(App app, Channel channel, String orderNo) {

        String sentence = "SELECT id AS id, app_id AS app_id, channel_id AS channel_id, amount AS amount, order_no AS order_no, request_no AS request_no, trade_no AS trade_no, card_no AS card_no, transaction_status AS transaction_status_as_int, last_modified_time AS last_modified_time FROM transaction_record WHERE app_id = :appId AND channel_id = :channelId AND order_no = :orderNo AND transaction_status >= :transactionStatusAsInt";

        Map<String, Object> parameters = new HashMap<String, Object>(4);
        parameters.put("appId", app.getId());
        parameters.put("channelId", channel.getId());
        parameters.put("orderNo", orderNo);
        parameters.put("transactionStatusAsInt", TransactionStatus.PAID.getCode());

        List<TransactionRecord> transactionRecords = genericJdbcSupport.queryForList(sentence, parameters, TransactionRecord.class);
        return CollectionUtils.isEmpty(transactionRecords) ? null : transactionRecords.get(0);
    }

    /* (non-Javadoc)
     * @see cn.xcm.payment.service.TransactionRecordService#changeTransactionStatus(cn.xcm.payment.entity.transaction.TransactionRecord, cn.xcm.payment.entity.transaction.TransactionStatus, java.lang.String)
     */
    public void changeTransactionStatus(TransactionRecord transactionRecord, TransactionStatus transactionStatus, String tradeNo) {
        // Change transaction record
        Date modifiedTime = new Date();
        String sentence = "UPDATE transaction_record SET transaction_status = :transactionStatusAsInt, trade_no = :tradeNo, last_modified_time = :lastModifiedTime WHERE id = :id";
        genericJdbcSupport.executeSQL(sentence, transactionRecord.tradeNo(tradeNo).transactionStatus(transactionStatus).lasModifiedTime(modifiedTime));

        // Create transaction log
        transactionRecordDaoSupport.createTransactionLog(new TransactionLog(transactionRecord, transactionStatus.getOperationType()));
    }

    /* (non-Javadoc)
     * @see co.fitstart.payment.service.TransactionRecordService#changeTransactionStatus(co.fitstart.payment.entity.transaction.TransactionRecord, co.fitstart.payment.entity.transaction.TransactionStatus)
     */
    public void changeTransactionStatus(TransactionRecord transactionRecord, TransactionStatus transactionStatus) {
        // Change transaction record
        Date modifiedTime = new Date();
        String sentence = "UPDATE transaction_record SET transaction_status = :transactionStatusAsInt, last_modified_time = :lastModifiedTime WHERE id = :id";
        genericJdbcSupport.executeSQL(sentence, transactionRecord.transactionStatus(transactionStatus).lasModifiedTime(modifiedTime));

        // Create transaction log
        transactionRecordDaoSupport.createTransactionLog(new TransactionLog(transactionRecord, transactionStatus.getOperationType()));
    }

    /* (non-Javadoc)
     * @see co.fitstart.payment.service.TransactionRecordService#createTransactionLog(co.fitstart.payment.entity.transaction.TransactionRecord, co.fitstart.payment.entity.transaction.OperationType)
     */
    public void createTransactionLog(TransactionRecord transactionRecord, OperationType operationType) {
        transactionRecordDaoSupport.createTransactionLog(new TransactionLog(transactionRecord, operationType));
    }

}
